﻿using System;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;

namespace Ganaye.EmbeddedDb
{
    public class ColumnDefinition : ISimpleSerializable
    {
        private string mColumnName;
        public char mDbfColumnType;
        public ColumnType mColumnType;
        internal int mSizeMin, mSizeMax;
        private byte mColumnSize;
        private byte mColumnDecimals;
        internal Type mFieldType;
        internal bool mIsVariableSize;
        private bool mCanHaveDecimals;
        public PropertyInfo BaseProperty;

        public ColumnDefinition()
        {
            //used by serializer
        }

        public ColumnDefinition(string columnName, ColumnType type)
        {
            mColumnName = columnName;
            mDbfColumnType = (char)type;
            mColumnType = type;

            switch (type)
            {
                case ColumnType.DBASE3_CHARACTER:
                    Size = 60;
                    mIsVariableSize = true;
                    mSizeMin = 1;
                    mSizeMax = 255;
                    break;
                case ColumnType.DBASE3_NUMBER:
                    Size = 6;
                    mIsVariableSize = true;
                    mSizeMin = 1;
                    mSizeMax = 255;
                    mCanHaveDecimals = true;
                    break;
                case ColumnType.DBASE3_LOGICAL: 
                    Size = 1;
                    break;
                case ColumnType.DBASE3_DATE:
                    Size = 8;
                    break;
                case ColumnType.DBASE3_MEMO:
                    Size = 10;
                    break;
                case ColumnType.DBASE4_FLOAT:
                    Size = 20;
                    break;
                case ColumnType.DBASE5_BINARY:
                case ColumnType.DBASE5_GENERAL:
                case ColumnType.FOXPRO_PICTURE:
                    Size = 10;
                    break;
                case ColumnType.FOXPRO_DATETIME:
                case ColumnType.FOXPRO_CURRENCY:
                case ColumnType.FOXPRO_VARCHAR:
                case ColumnType.FOXPRO_AUTOINC:
                    throw new RuntimeException(string.Format("Unsupported type {0}", type.ToString())); // unknown
                    
                case ColumnType.FOXPRO_INTEGER:
                    Size = 4;
                    break;
                case ColumnType.FOXPRO_VARIFIELD:
                    mIsVariableSize = true;
                    Size = 10;
                    mSizeMin = 2;
                    mSizeMax = 10;
                    break;
                case ColumnType.FOXPRO_TIMESTAMP:
                    Size = 8;
                    break;
                case ColumnType.FOXPRO_DOUBLE:
                    Size = 8;
                    break;
            }
        }

        public string ColumnName
        {
            get
            {
                return mColumnName;
            }
        }

        public ColumnType ColumnType
        {
            get { return mColumnType; }
        }

        public byte Size
        {
            get { return mColumnSize; }
            set { mColumnSize = value; }
        }

        public byte Decimals
        {
            get { return mColumnDecimals; }
            set { mColumnDecimals = value; }
        }

        public bool Nullable { get; set; }

        public Type FieldType
        {
            get {
                return mFieldType; 
            }
            set { mFieldType = value; }
        }

        private Type PreferedNativeType()
        {
            switch (ColumnType)
            {
                case ColumnType.DBASE3_CHARACTER:
                case ColumnType.DBASE3_MEMO:        // String
                case ColumnType.FOXPRO_VARCHAR:
                    return typeof(string);
                case ColumnType.DBASE3_LOGICAL:
                    return typeof(bool?); //typeof(bool?);
                case ColumnType.DBASE3_NUMBER:
                    if (Decimals > 0)
                        return typeof(double?);//typeof(double?);
                    else
                        return typeof(Int32?);//typeof(Int32?);
                case ColumnType.FOXPRO_INTEGER:
                case ColumnType.FOXPRO_AUTOINC:
                    return typeof(Int32?);//typeof(Int32?);
                case ColumnType.DBASE3_DATE:      // DateTime
                case ColumnType.FOXPRO_DATETIME:
                case ColumnType.FOXPRO_TIMESTAMP:
                    return typeof(DateTime?);//typeof(DateTime?);
                case ColumnType.DBASE4_FLOAT:       // Double
                case ColumnType.FOXPRO_DOUBLE:
                    return typeof(double?); //typeof(double?);
                case ColumnType.DBASE5_BINARY:
                case ColumnType.DBASE5_GENERAL:
                case ColumnType.FOXPRO_PICTURE:
                    return typeof(byte[]);
                case ColumnType.FOXPRO_CURRENCY:
                case ColumnType.FOXPRO_VARIFIELD:
                case ColumnType.UNKNOWN:
                default:
                    throw new RuntimeException(string.Format("Unsupported ColumnType {0}", ColumnType.ToString()));
            }
        }

        #region ISimpleSerializable Members

        public void Serialize(SimpleSerializer context)
        {
            context.SerializeString(ref mColumnName, 11, ' ');
            context.SerializeChar(ref mDbfColumnType);
            context.Skip(4); // FieldDataAddress
            context.SerializeByte(ref mColumnSize);
            context.SerializeByte(ref mColumnDecimals);
            context.Skip(14);
            if (context.IsFromBytes())
            {
                mColumnType = (ColumnType)mDbfColumnType;

            }
            
        }

        public int SerializationBufferSize()
        {
            return 32;
        }

        #endregion


        internal void GuessFieldType()
        {
            mFieldType = PreferedNativeType();
        }
    }

}

